#ifndef PUDGE_META_VISITOR_HPP
#define PUDGE_META_VISITOR_HPP

#include <boost/preprocessor/arithmetic/dec.hpp>
#include <boost/preprocessor/iteration/iterate.hpp>
#include <boost/preprocessor/repetition/enum.hpp>
#include <boost/preprocessor/repetition/enum_params.hpp>
#include <boost/preprocessor/repetition/repeat.hpp>

#ifndef PUDGE_META_VISITOR_PARAMS
#define PUDGE_META_VISITOR_PARAMS 5
#endif

namespace pudge { namespace meta {

	struct null_client_type {};

#define PUDGE_META_VISITOR_M(Z,N,A) class T ## N = null_client_type
	template<BOOST_PP_ENUM(PUDGE_META_VISITOR_PARAMS, PUDGE_META_VISITOR_M, )>
#undef PUDGE_META_VISITOR_M
	struct visitor
	{
		typedef visitor<BOOST_PP_ENUM_PARAMS(PUDGE_META_VISITOR_PARAMS, T)> self_type;

		virtual ~visitor() {}

#define PUDGE_META_VISITOR_M(Z,N,A) virtual void visit(T ## N & t) { }
		BOOST_PP_REPEAT(PUDGE_META_VISITOR_PARAMS, PUDGE_META_VISITOR_M, );
#undef PUDGE_META_VISITOR_M

		template<class Function> struct function : self_type
		{
			typedef Function function_type;
			typedef self_type visitor_type;
			Function & function_;
			function(Function & function__) : function_(function__){}
			virtual ~function() {}

#define PUDGE_META_VISITOR_M(Z,N,A) virtual void visit(T ## N & t) { function_(t); }
			BOOST_PP_REPEAT(PUDGE_META_VISITOR_PARAMS, PUDGE_META_VISITOR_M, );
#undef PUDGE_META_VISITOR_M
		};

		struct client
		{
			typedef self_type visitor_type;
			virtual ~client(){}
			virtual void accept(visitor_type & visitor_) = 0;
			template<class Visitor> void operator()(Visitor & visitor_)
			{
				accept(self_type::function<Visitor>(visitor_));
			}
		};
		template<class T> struct clientd : client
		{
			virtual ~clientd() {}
			virtual void accept(visitor_type & visitor_)
			{
				visitor_.visit(*static_cast<T*>(this));
			}
		};
		template<class T> struct clientr : client
		{
			T & value;
			clientr(T & t) : value(t) {}
			virtual ~clientr() {}
			virtual void accept(visitor_type & visitor_)
			{
				visitor_.visit(value);
			}
		};
		template<class T> struct clientv : client
		{
			T value;
			clientv(T & t) : value(t) {}
			virtual ~clientv() {}
			virtual void accept(visitor_type & visitor_)
			{
				visitor_.visit(value);
			}
		};
	};

#define BOOST_PP_FILENAME_1 "pudge/meta/detail/unroll_visitor.hpp"
#define BOOST_PP_ITERATION_LIMITS (1,BOOST_PP_DEC(PUDGE_META_VISITOR_PARAMS))
#include BOOST_PP_ITERATE()

}}
#endif